iT邦幫忙

2024 iThome 鐵人賽

DAY 11
1
Modern Web

Vue 3 初學者:用實作帶你看過核心概念系列 第 11

Vue 3 用實作帶你看過核心概念 - Day 11:v-for 指令的基本與進階應用

  • 分享至 

  • xImage
  •  

文章背景圖

目錄

  • v-for 基本使用
  • v-for 就地更新 - 使用 key 的重要性
  • v-for 指令參數介紹
  • v-for 指令結合 template 標籤及計算屬性應用情境
  • v-for 迭代純數字
  • v-for 結合自定義方法 - 雙層陣列搭配方法篩選
  • 總結
  • 小試身手

v-for 基本使用

當數據類型是陣列或物件時,我們不會希望將重複的選項或元素直接硬編碼在模板中。這樣的寫法會導致代碼冗長且不具可維護性。

這時候就會使用v-for指令,在模板中可以根據陣列或物件的特性,動態地迴圈渲染每個選項。

⭐ 在使用v-for指令時,需要為元素綁定一個唯一的keykey建議使用基礎型別的值,且不要使用索引作為 key,以確保渲染效率和狀態一致性。

以下透過印出產品清單案例解釋使用v-for與不使用v-for的差別:

👉 Vue3 Options API v-for 陣列基本應用實作連結

未使用v-for指令,只能一個一個條列出想顯示的項目:

Vue Template:

<ul>
  <li>{{ productList[0] }}</li>
  <li>{{ productList[1] }}</li>
  <li>{{ productList[2] }}</li>
</ul>

使用v-for指令,免除重複冗長的程式碼:
Vue Template:

<ul>
  <li v-for="item in productList">
      {{ item }}
  </li>
</ul>

JavaScript:

const rootComponent = {
  data() {
    return {
      productList: ["漢堡", "熱狗", "法式吐司"]
    };
  }
};

v-for 就地更新 - 使用 key 的重要性

當使用v-for指令時,Vue 採用一種稱為「就地更新」(in-place patch)的策略。這意味著當數據變更時,Vue 會優先只更新實際改變的部分,而不會重新創建或移動 DOM 元素。這種策略能有效減少不必要的 DOM 操作,從而顯著提升渲染效能。

然而,這種策略僅適用於列表項目本身不依賴內部臨時狀態的情況。如果列表項目中包含需要保留的臨時狀態(例如表單輸入值或暫存數據),則必須使用 key 屬性來確保元素能正確更新與渲染。

以下分別透過表單輸入值跟核取方塊(Checkbox)臨時狀態舉例說明:

⭐ 特別注意這兩個範例都沒有在v-for指令使用的元素上綁定key屬性

核取方塊(Checkbox)殘留臨時狀態的案例:

👉 Vue3 Options API v-for 指令臨時性狀態(checkbox) 未綁定 key 屬性異常實作連結

流程說明:點選第一個家事或第二個買菜,當陣列順序發生改變的時候,臨時狀態會殘留在上面。

checkbox 臨時狀態未綁定 key

發生此異常的原因如下:

Vue 就地變更策略(解釋臨時性狀態殘留問題)

由於 Vue 的就地更新策略,導致事件 C 的內容被替換至事件 B事件 B 的內容則替換到事件 A 上。這使得原本事件 A 的勾選狀態被保留並錯位到新的內容上,導致勾選的臨時狀態與內容不匹配,最終出現不符合預期的情況。

解決方式:在v-for指令的元素上,使用唯一值id綁定key屬性。

Vue Template:

  <div class="pendingList">
    <h2>代辦清單</h2>
    <ul>
      <li v-for="(data, index) in pendingList">
        事件:{{ data.name }} - 時間:{{ data.time }} 分鐘
        <label><input type="checkbox" v-model="data.isDone"></label>
      </li>
    </ul>
  </div>
  
  <div class="completedList">
    <h2>已完成清單</h2>
    <ul>
      <li v-for="(data, index) in downList">
        事件:{{ data.name }} - 時間:{{ data.time }} 分鐘
        <label><input type="checkbox" v-model="data.isDone"></label>
      </li>
    </ul>
  </div>

JavaScript:

const rootComponent = {
  data() {
    return {
      todoList: [
        {
          id: 0,
          name: "做家事",
          time: 20,
          isDone: false
        },
        {
          id: 1,
          name: "買菜",
          time: 40,
          isDone: false
        },
        {
          id: 2,
          name: "看書",
          time: 60,
          isDone: false
        }
      ]
    };
  },
  computed: {
    pendingList() {
      return this.todoList.filter((item) => item.isDone === false);
    },
    downList() {
      return this.todoList.filter((item) => item.isDone !== false);
    }
  }
};

表單輸入值(input)殘留臨時狀態的案例:

👉 Vue3 Options API v-for 表單輸入框輸入狀態未綁定 key 屬性異常實作連結

流程說明:

  1. 在每個餐點旁邊輸入框輸入對應的英文名稱
  2. 點擊反轉陣列按鈕,改變陣列內項目的順序

表單輸入未綁定 key 臨時狀態殘留

由於未綁定key屬性,當項目順序變更時,Vue 只會替換元素的內容,導致輸入框的臨時狀態被保留並錯位,從而產生與預期不符的顯示結果。

解決方式:在v-for指令的元素上,使用項目值綁定key屬性。

Vue Template:

  <div class="container">
    <h3>v-for 未加上key的使用</h3>
    <ul>
      <li v-for="(item, index) in productList">
        (未加上key)索引值:{{ index }} - 產品名稱:{{ item }}
        <input type="text">
      </li>
    </ul>
    <button type="button" @click="reverseArray()">陣列反轉按鈕</button>
  </div>

JavaScript:

const rootComponent = {
  data() {
    return {
      productList: ["漢堡", "熱狗", "法式吐司"]
    };
  },
  methods: {
    reverseArray() {
      this.productList.reverse();
    }
  }
};

v-for 指令參數介紹

  • 陣列使用參數說明:參數順序分別是迭代項目索引值
  • 物件使用參數說明:參數順序分別是屬性值屬性名稱索引值

👉 Vue3 Options API v-for 指令參數介紹實作連結

Vue Template

  <div class="card">
    <h2>陣列綁定使用(參數:迭代項、索引值)</h2>
    <!--     按照索引順序 -->
    <ul>
      <li v-for="(item, index) in likeFood">item:{{ item }} - index:{{ index}}</li>
    </ul>
  </div>
  
  <div class="card">
    <h2>物件綁定使用(參數:屬性值、屬性名稱、索引值)</h2>
    <!--     按照 Object.values() 順序 -->
    <ul>
      <li v-for="(value, key, index) in author">value:{{ value }} - key:{{ key }} - index:{{ index }}</li>
    </ul>
  </div>

JavaScript:

const rootComponent = {
  data() {
    return {
      likeFood: ["頻果", "芭樂", "香蕉"],
      author: { id: 0, name: "John", age: 24 }
    };
  }
};

v-for 指令結合 template 標籤及計算屬性應用情境

Vue3 中 v-if 優先級大於 v-for。同時使用在同一元素是非常不建議的。

Vue Template:

<li v-for="item in likeFood" v-if="item !== '蘋果'">
    {{ item }}
</li>

由於v-if的優先級較高,會導致在條件判斷之前無法正常讀取 item 的值,從而導致 Vue 拋出警告,並影響渲染結果。

image

解決方式可以透過<template>包裹元素在外層,進而避免v-ifv-for指令同時在一個元素上使用。

👉 Vue Options API template 及計算屬性搭配 v-for 基本使用實作連結

HTML:

<template v-for="item in likeFood">
  <li v-if="item !== '蘋果'">
    {{ item }}
  </li>
</template>

雖然這裡可以使用template 標籤來處理,但這種方式看起來沒有預期的那麼直觀和易於理解。由於框架的核心特性是數據驅動,另一種更優的方法是通過預先處理數據,並結合Vue 指令來簡化邏輯,從而減少模板的複雜度。

接下來,我們可以嘗試使用計算屬性來進行改寫:

Vue Template:

<li v-for="item in getFilterFood">
{{ item }}
</li>

javaScript:

  computed: {
    getFilterFood() {
      return this.likeFood.filter((item) => item !== "蘋果");
    }
  }

這邊我有準備另一個更適合使用template標籤的案例。

表格渲染區塊要顯示不同內容:

👉 Vue3 Options API v-for 表格搭配 Template 使用實作連結

流程說明:將商品列表用表格方式呈現。如果有商品額外細節說明(EX:Very sour)表格要顯示,沒有則不用。

表格 v-for 指令搭配 template 標籤使用

HTML:

  <table>
    <thead>
      <tr>
        <th>id</th>
        <th>名稱</th>
        <th>價格</th>
      </tr>
    </thead>
    <tbody>
      <template v-for="item in productList">
        <tr>
          <td>{{ item.id }}</td>
          <td>{{ item.name }}</td>
          <td>{{ item.price }}</td>
        </tr>
        <tr v-if="item.detail">
          <td colspan=3>{{ item.detail.msg }}</td>
        </tr>
      </template>
    </tbody>
  </table>

javaScript:

const rootComponent = {
  data() {
    return {
      productList: [
        {
          id: 1,
          name: "orange",
          price: 50,
          detail: { msg: "Very sour " }
        },
        {
          id: 2,
          name: "banana",
          price: 30
        }
      ]
    };
  }
}

v-for 迭代純數字

除了用來迭代陣列和物件之外,v-for 指令還可以直接用於純數字的迭代。這樣的用法可以用來生成特定次數的元素。

流程說明:生成固定數量的元素。

👉 Vue3 Options API v-for 純數字迭代使用實作連結

<div id="app">
  <ul>
    <li v-for="n in 10">
      數字:{{ n }}
    </li>
  </ul>
</div>

v-for 結合自定義方法 - 雙層陣列搭配方法篩選

當面對雙層陣列的數據時,若需要從不同陣列中進行篩選,可以透過v-for指令結合方法來實作。

但是在操作數據時,要特別小心避免直接修改原始數據(例如使用sortreverse會改變元數據)。若必須使用這些方法,建議先對數據進行拷貝再操作,以避免潛在的副作用。

流程說明:

  1. 使用v-for指令迭代第一層陣列,取得每個子陣列。
  2. 在每個子陣列中,透過自定義方法篩選出低於 350 元的便宜商品,並將結果回傳到模板中進行渲染。

👉 Vue3 Options API v-for 雙迴圈搭配方法實作連結

HTML:

<div id="app">
  <ul>
    <li v-for="(product, index) in productList" :key="product">
      第{{ index }}個陣列產品清單中便宜商品:
      <p v-for="cheapProduct in getCheapProducts(product)">
        {{ cheapProduct}}
      </p>
    </li>
  </ul>
</div>

javaScript:

const rootComponent = {
  data() {
    return {
      productList: [
        [
          { id: 1, name: "滑鼠", price: 100 },
          { id: 2, name: "鍵盤", price: 350 },
          { id: 3, name: "電腦", price: 250 }
        ],
        [
          { id: 4, name: "耳機", price: 200 },
          { id: 5, name: "音響", price: 400 },
          { id: 6, name: "麥克風", price: 650 }
        ]
      ]
    };
  },
  methods: {
    getCheapProducts(productArray) {
      // 需避免使用影響元數據的方法(filter 本身就會回傳新的陣列)
      return productArray.filter((item) => item.price < 350);
    }
  }
}

總結

  • v-for 可以結合實例定義的數據、物件、計算屬性及方法的回傳值進行渲染
    • 陣列應用:顯示每個迭代項索引值
    • 物件應用:顯示每個物件屬性值屬性名稱索引值
    • 純數字迭代:直接迭代數字範圍,生成指定次數的元素。
  • v-for 綁定 key 的重要性v-for中使用key屬性,能夠確保元素的唯一性,避免因為就地更新策略導致的錯誤渲染(特別是臨時狀態的判定)。
  • v-if 與 v-for 不建議同時使用v-if的優先級高於v-for,因此應避免在同一元素上同時使用,這會導致元素在條件判斷之前無法讀取v-for迭代項的值。若需要使用可以搭配template標籤使用。
  • v-for 結合自定義方法v-for指令可以與自定義方法結合使用進行篩選。需注意避免對元數據的直接操作,應對數據進行拷貝後再進行處理。

小試身手

使用今天學習到的v-for指令,試著將雙層陣列的基數給印出來。(僅需調整模板的部分搭配使用v-for指令即可)

👉 Vue3 Options API 基數數字練習模板連結
👉 Vue3 Options API 基數數字完成實作連結


上一篇
Vue 3 用實作帶你看過核心概念 - Day 10:條件渲染 - v-if 與 v-show 的區別與應用
下一篇
Vue 3 用實作帶你看過核心概念 - Day 12:v-on 指令 - 事件處理
系列文
Vue 3 初學者:用實作帶你看過核心概念14
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言